package com.mycity.semantic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import com.mycity.bean.Post;
import com.mycity.dao.PostDao;
import com.mycity.services.ProfileService;
import com.mycity.services.SearchService;
import com.mycity.util.MapUtil;
import com.mycity.util.TagUtil;

/**
 * The UserRelation class represents a recommendation system by finding relations between users and posts. 
 * @author  Ahmet Erdem
 * @version December 2013 
 */
public class UserRelation {
	static ProfileService profileService = new ProfileService();
	static SearchService searchService = new SearchService();

	private static final int LOCATION_LIMIT = 3;

	/**
	 * Finds recommend posts for a user by checking his related tags and locations.
	 * @param userId id of the user who is looking for recommended posts.
	 * @return list of posts recommended for the specific user
	 */
	public static List<Post> recommend(int userId){
		HashMap<String,Double> relatedTags = relatedTags(userId);
		List<Integer> relatedLocations = relatedLocations(userId);
		HashMap<Post, Double> postsWithScores = new HashMap<Post, Double>();

		int numLocations = Math.min(relatedLocations.size(), LOCATION_LIMIT);

		List<Post> recommendedPosts = new ArrayList<Post>();
		for(int i=0; i<numLocations; i++)
			recommendedPosts.addAll(searchService.searchByLocation(relatedLocations.get(i)));

		for(int i=0; i<recommendedPosts.size(); i++){
			Post post = recommendedPosts.get(i);
			if(post.getUserId()!=userId){
				double score = 0;
				String tags[] =TagUtil.separate(post.getTags());
				if(tags!=null){
					for(int j=0; j<tags.length; j++){
						if(relatedTags.containsKey(tags[j]))
							score+=relatedTags.get(tags[j]);
					}
				}
				postsWithScores.put(post, score);
			}
		}

		Map<Post, Double> result= MapUtil.sortByValue(postsWithScores);
		List<Post> list = new ArrayList<Post>(result.keySet());
		return list;

	}

	/**
	 * Finds related tags for a user by checking his past interactions.
	 * These interactions are making a post and giving diamond to a post.
	 * Semantically related tags are also found with a similarity score.
	 * @param userId id of the user who is looking for recommended posts.
	 * @return map of tags with their similarity scores
	 */
	public static HashMap<String, Double> relatedTags(int userId){
		List<Post> relatedPosts = relatedPosts(userId);
		HashMap<String, Double> tagsWithScores = new HashMap<String, Double>();
		for(int i=0; i<relatedPosts.size(); i++){
			String tags[] =TagUtil.separate(relatedPosts.get(i).getTags());
			if(tags!=null){
				for(int j=0; j<tags.length; j++){
					String relatedTags[] = new String[Freebase.LIMIT+1];
					Double scores[] = new Double[Freebase.LIMIT+1];
					int length = Freebase.setScores(tags[j], relatedTags, scores);

					for(int k=0; k<length; k++){
						if(tagsWithScores.containsKey(relatedTags[k])){
							double score = tagsWithScores.get(relatedTags[k])+scores[k];
							tagsWithScores.remove(relatedTags[k]);
							tagsWithScores.put(relatedTags[k], score);
						}
						else
							tagsWithScores.put(relatedTags[k], scores[k]);

					}
				}
			}
		}

		return tagsWithScores;
	}

	/**
	 * Finds related locations for a user by checking his past interactions.
	 * These interactions are making a post and giving diamond to a post.
	 * @param userId id of the user who is looking for recommended posts.
	 * @return list of location id (id used by Instagram API)
	 */
	public static List<Integer> relatedLocations(int userId){
		List<Post> relatedPosts = relatedPosts(userId);
		Map<Integer, Integer> locationsWithScores = new HashMap<Integer, Integer>();

		for(int i=0; i<relatedPosts.size(); i++){
			int locationId = relatedPosts.get(i).getLocationId();
			if(locationsWithScores.containsKey(locationId)){
				int score = locationsWithScores.get(locationId)+1;
				locationsWithScores.remove(locationId);
				locationsWithScores.put(locationId, score);
			}
			else
				locationsWithScores.put(locationId, 1);
		}


		Map<Integer, Integer> result= MapUtil.sortByValue(locationsWithScores);
		List<Integer> list = new ArrayList<Integer>(result.keySet());
		return list;
	}

	/**
	 * Finds related posts for a user by checking his past interactions.
	 * These interactions are making a post and giving diamond to a post.
	 * @param userId id of the user who is looking for recommended posts.
	 * @return list of posts which the user has positive interaction.
	 */
	private static List<Post> relatedPosts(int userId){
		List<Post> diamondedPosts = null;
		List<Post> usersPosts = null;
		try {
			usersPosts = profileService.getAllPostsByUserId(userId);
			diamondedPosts = profileService.diamondedPosts(userId);
		} catch (Exception e) {
			return null;
		}
		usersPosts.addAll(diamondedPosts);
		return usersPosts;
	}
}
